<template>
  <div>
    <!--    坐标集群-->
    <div id="maps"></div>
    <div ref="popup" class="popup" v-show="shopPopup">
      <div class="info">
        <ul>
          <li>信息1：xxx</li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
import {Map, View, Feature, Overlay} from "ol"  //Feature类 ---->要素，即地图上的几何对象，包括点Point、线LineString、多边形Polygon、圆Circle,用ol.interaction.Drew绘制
import XYZ from "ol/source/XYZ"; //xyz的方式加载地图    x-->瓦片行号；y-->瓦片列号；z-->瓦片级数
import * as olProj from "ol/proj" //地图中的坐标系相互转换的包
import {Style, Fill, Stroke, Text, Circle as sCircle} from "ol/style";  //ol/style 地图样式 Fill-->要填充要素的样式  Stroke-->要素边界样式  Circle-->针对矢量要素设置圆形样式
import {Point} from "ol/geom";
import {Tile as TileLayer, Vector as VectorLayer} from "ol/layer";//Tile瓦片 Vector矢量图层
import {Cluster, Vector as VectorSource} from "ol/source"  //矢量数据
import {boundingExtent} from "ol/extent";

export default {
  name: "multiple-points",
  data() {
    return {
      openMap: null,
      shopPopup: false,
      popup: null,
      markerLayer: null,//坐标标记层
      markerSource: null,// 坐标数据源
    }
  },
  mounted() {
    this.initMap()
  },
  methods: {
    initMap() {
      this.openMap = new Map({
        target: "maps",
        layers: [
          new TileLayer({
            source: new XYZ({
              //openMap的瓦片地址
              url: "https://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}"
            })
          })
        ],
        view: new View({
          center: olProj.fromLonLat([108.945951, 34.465262]),  //fromLonLat(coordinate,destination) 把当前坐标coordinate转到目标投影坐标系destination
          zoom: 12
        }),
        controls: []
      })
      this.setMarker()
      this.singleClick()
      this.pointerMove()
      this.addOverlay()
      this.resolutionChange();
    },
    setMarker() {
      this.setMarkerSource()
      let _style = this.setClusterStyle();
      this.markerLayer = new VectorLayer({  //矢量图层
        source: this.markerSource,
        style: _style,
      })
      this.openMap.addLayer(this.markerLayer)
    },
    getPoints() {
      return [
        [108.945951, 34.465262],
        [109.04724, 34.262504],
        [108.580321, 34.076162],
        [110.458983, 35.071209],
        [105.734862, 35.49272],
      ]
    },
    setMarkerSource() {
      let _style = new Style({
        image: new sCircle({
          radius: 10,
          stroke: new Stroke(({
            color: "red"
          })),
          fill: new Fill({
            color: 'black'
          })
        })
      })
      let _points = this.getPoints()
      let _features = _points.reduce((list, item) => {
        let _feature = new Feature({
          geometry: new Point(olProj.fromLonLat(item))
        })
        _feature.setStyle(_style)
        list.push(_feature)
        return list
      }, [])
      //Cluster 聚合类  聚合就是地图放大缩小
      this.markerSource = new Cluster({
        distance: 100,  // 聚合点与点之间的距离，标注间距离小于这个值时进行聚合
        source: new VectorSource({
          features: _features
        })
      })
    },
    setClusterStyle() {
      const styleCache = {};  //用于保持特定数量的聚合群的要素样式
      const _style = (feature) => {
        //features 绘制要素的目标集合  feature.get(key)--->读取属性信息
        const size = feature.get("features").length; // 获取要素所在聚合群的要素数量
        let style = styleCache[size];
        if (!style) {
          if (size > 1) { // 非聚合的点不显示数字
            style = new Style({
              image: new sCircle({   // image 矢量图层被渲染为图像，会跟着视图旋转
                radius: 20,
                stroke: new Stroke({
                  color: "black",
                }),
                fill: new Fill({
                  color: "red",
                }),
              }),
              text: new Text({
                text: size.toString(),
                fill: new Fill({
                  color: "#fff",
                }),
              }),
            });
          } else {
            style = new Style({
              image: new sCircle({
                radius: 15,
                stroke: new Stroke({
                  color: "#fff",
                }),
                fill: new Fill({
                  color: "#e9b626",
                }),
              }),
            });
          }
          styleCache[size] = style;
        }
        return style;
      };
      return _style;
      //  要添加自定义标注---用set("key","value","boolean"),用feature.get("features").length判断
    },

    //集群点击事件
    singleClick() {
      this.openMap.on("singleclick", (e) => {
        //获取选中像素信息
        this.markerLayer.getFeatures(e.pixel).then((clickedFeatures) => {
          if (clickedFeatures.length) {
            const features = clickedFeatures[0].get("features")  //获取当前聚合的所有feature
            if (features.length > 1) {
              const extent = boundingExtent( //boundingExtent --获取多边形边界，返回值是[minx, miny, maxx, maxy]([横坐标最小值，纵坐标最小值，横坐标最大值，纵坐标最大值])
                  //getGeometry()获取默认几何属性  getCoordinates()返回线段坐标
                  features.map((r) => r.getGeometry().getCoordinates())   //获取元素的经纬度坐标
              )
              //getView()获取地图视图。视图管理中心和分辨率等属性
              //fit()定位方法  可设置宽高等属性 duration 动画时长  padding 做偏移，让视线定位在我们中心
              this.openMap.getView().fit(extent, {duration: 100, padding: [200, 200, 200, 200]})  //缩放到给定的点和分辨率
            } else {
              this.shopPopup = true;
              //  设置弹窗位置
              let coordinates = features[0].getGeometry().getCoordinates()
              this.popup.setPosition(coordinates)
            }
          } else {
            this.shopPopup = false;
          }
        })
      })
    },
    resolutionChange() {
      //  监听缩放
      //resolution----->视图分辨率
      this.openMap.getView().on("change:resolution",()=>{
        this.shopPopup = false
      })
    },
    //  鼠标移动到点位的光标样式
    pointerMove() {
      this.openMap.on("pointermove", (e) => {  //pointermove 地图平移时触发/鼠标移动时触发
        if (this.openMap.hasFeatureAtPixel(e.pixel)) {  //通过e.pixel判断在视图地图上是否有相交的像素，有的话改变鼠标样式
          this.openMap.getViewport().style.cursor = "pointer";  //getViewport() 获取作为map视图的元素
        } else {
          this.openMap.getViewport().style.cursor = "inherit"
        }
      })
    },
    addOverlay() {
      //  创建Overlay ----->  要显示在地图上方，且附加到单个地图位置的元素（不在地图上固定位置，要绑定在地理坐标上）
      let elPopup = this.$refs.popup
      this.popup = new Overlay({
        element: elPopup,  // element Dom元素
        positioning: "bottom-right", // 设置overlay相对于坐标的位置
        stopEvent: false, // 是否应该停止到map视图的事件传播，若为true，在map层用on监听的事件都会无用
        //true--->Overlay层被放在装载控件的div容器里（该div容器css类名为ol-overlaycontainer-stopevent）
        //false--->Overlay层被放在css类名为ol-overlaycontainer的div容器下
        offset: [0, -20] //偏移量（水平偏移--左负右正，垂直偏移--上负下正）
      })
      this.openMap.addOverlay(this.popup)  // 将Overlay添加到地图中
    },
  }
}
</script>

<style scoped>
#maps {
  width: 100vw;
  height: 100vh;
}

.popup {
  width: 200px;
  background-color: white;
  padding: 18px;
  border-radius: 10px;
  box-shadow: 0 0 15px rgb(177, 177, 177);
}

.info {
  font-size: 14px;
  text-align: left;
}

ul {
  padding-left: 0;
}
</style>
